package com.hys.app.service.erp.impl;

import cn.hutool.core.util.StrUtil;
import com.hys.app.converter.erp.OrderConverter;
import com.hys.app.converter.erp.OrderItemConverter;
import com.hys.app.converter.erp.OrderPaymentConverter;
import com.hys.app.framework.database.WebPage;
import com.hys.app.framework.database.mybatisplus.base.BaseServiceImpl;
import com.hys.app.framework.exception.ServiceException;
import com.hys.app.framework.util.CurrencyUtil;
import com.hys.app.framework.util.DateUtil;
import com.hys.app.mapper.erp.OrderMapper;
import com.hys.app.model.base.context.Region;
import com.hys.app.model.base.context.RegionFormatter;
import com.hys.app.model.erp.dos.*;
import com.hys.app.model.erp.dto.*;
import com.hys.app.model.erp.enums.*;
import com.hys.app.model.erp.vo.OrderAllowable;
import com.hys.app.model.erp.vo.OrderVO;
import com.hys.app.model.goods.dos.CategoryDO;
import com.hys.app.model.system.dos.DeptDO;
import com.hys.app.service.erp.*;
import com.hys.app.service.goods.CategoryManager;
import com.hys.app.service.system.DeptManager;
import com.hys.app.service.system.RegionsManager;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.Collections;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.function.Function;

import static com.hys.app.framework.util.CollectionUtils.convertList;
import static com.hys.app.framework.util.CollectionUtils.convertMap;

/**
 * 订单业务层实现
 *
 * @author 张崧
 * 2024-01-24 15:58:31
 */
@Service
public class OrderManagerImpl extends BaseServiceImpl<OrderMapper, OrderDO> implements OrderManager {

    @Autowired
    private OrderConverter converter;

    @Autowired
    private OrderItemConverter itemConverter;

    @Autowired
    private MemberManager memberManager;

    @Autowired
    private WarehouseManager warehouseManager;

    @Autowired
    private MarketingManagerManager marketingManagerManager;

    @Autowired
    private StoreManager storeManager;

    @Autowired
    private ProductManager productManager;

    @Autowired
    private CollectingAccountManager collectingAccountManager;

    @Autowired
    private NoGenerateManager noGenerateManager;

    @Autowired
    private OrderItemManager orderItemManager;

    @Autowired
    private OrderPaymentConverter orderPaymentConverter;

    @Autowired
    private OrderPaymentManager orderPaymentManager;

    @Autowired
    private DeptManager deptManager;

    @Autowired
    private CategoryManager categoryManager;

    @Autowired
    private ProductStockManager productStockManager;

    @Autowired
    private FinanceItemManager financeItemManager;

    @Autowired
    private EnterpriseManager enterpriseManager;

    @Autowired
    private WarehouseEntryBatchManager warehouseEntryBatchManager;

    @Autowired
    private RegionsManager regionsManager;

    @Override
    public WebPage<OrderVO> list(OrderQueryParams queryParams) {
        WebPage<OrderDO> page = baseMapper.selectPage(queryParams);
        return converter.convertPage(page);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public String add(OrderDTO orderDTO) {
        // 校验参数
        check(orderDTO);

        // 保存订单
        OrderDO orderDO = converter.combination(orderDTO);
        orderDO.setSn(noGenerateManager.generate(NoBusinessTypeEnum.Order, orderDO.getDeptId()));
        orderDO.setStatus(OrderStatusEnum.WAIT_AUDIT);
        save(orderDO);

        // 保存订单项
        List<OrderItemDO> itemList = itemConverter.convert(orderDTO, orderDO);
        orderItemManager.saveBatch(itemList);

        // 保存支付信息
        List<OrderPaymentDO> collectingAccountList = orderPaymentConverter.convert(orderDO.getId(), orderDTO.getPaymentList(), orderDTO.getCollectingAccountMap());
        orderPaymentManager.saveBatch(collectingAccountList);

        return orderDO.getSn();
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void edit(OrderDTO orderDTO) {
        // 校验是否可以编辑
        OrderDO old = getById(orderDTO.getId());
        if (!new OrderAllowable(old).getEdit()) {
            throw new ServiceException("当前订单状态不允许进行编辑操作");
        }

        // 校验参数
        check(orderDTO);

        // 更新订单
        OrderDO orderDO = converter.combination(orderDTO);
        updateById(orderDO);
        if (orderDO.getPaymentStatus() == OrderPaymentStatusEnum.NOT_PAY) {
            lambdaUpdate().set(OrderDO::getPaymentTime, null).eq(OrderDO::getId, orderDO.getId()).update();
        }

        // 更新订单项
        List<OrderItemDO> itemList = itemConverter.convert(orderDTO, orderDO);
        orderItemManager.deleteByOrderId(orderDO.getId());
        orderItemManager.saveBatch(itemList);

        // 保存支付信息
        List<OrderPaymentDO> paymentList = orderPaymentConverter.convert(orderDO.getId(), orderDTO.getPaymentList(), orderDTO.getCollectingAccountMap());
        orderPaymentManager.deleteByOrderId(orderDO.getId());
        orderPaymentManager.saveBatch(paymentList);
    }

    @Override
    public OrderVO getDetail(Long id) {
        OrderDO orderDO = getById(id);

        // 查询订单项
        List<OrderItemDO> itemList = orderItemManager.listByOrderIds(Collections.singletonList(id));
        // 查询库存（编辑时回显剩余库存）
        Map<Long, Integer> stockNumMap;
        if (orderDO.getType() == OrderTypeEnum.TO_B) {
            List<Long> warehouseId = Collections.singletonList(orderDO.getWarehouseId());
            List<Long> productIds = convertList(itemList, OrderItemDO::getProductId);
            stockNumMap = productStockManager.queryStockNum(warehouseId, productIds);
        } else {
            List<Long> batchIds = convertList(itemList, OrderItemDO::getBatchId);
            stockNumMap = warehouseEntryBatchManager.queryRemainStockNum(batchIds);
        }

        // 查询支付信息
        List<OrderPaymentDO> paymentList = orderPaymentManager.listByOrderId(id);
        // 查询门店信息
        StoreDO storeDO = orderDO.getDeliveryType() == OrderDeliveryType.self_pick ? storeManager.getById(orderDO.getStoreId()) : null;

        return converter.convert(orderDO, itemList, paymentList, storeDO, stockNumMap);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void delete(List<Long> ids) {
        removeBatchByIds(ids);
    }

    @Override
    public void warehouseOut(List<Long> ids, Long warehouseOutId) {
        lambdaUpdate()
                .set(OrderDO::getWarehouseOutFlag, true)
                .set(OrderDO::getStatus, OrderStatusEnum.WAIT_SHIP)
                .set(OrderDO::getWarehouseOutId, warehouseOutId)
                .in(OrderDO::getId, ids)
                .update();
    }

    @Override
    public void warehouseOutDelete(List<Long> warehouseOutIds) {
        lambdaUpdate()
                .set(OrderDO::getWarehouseOutFlag, false)
                .set(OrderDO::getStatus, OrderStatusEnum.WAIT_WAREHOUSE_OUT)
                .set(OrderDO::getWarehouseOutId, null)
                .in(OrderDO::getWarehouseOutId, warehouseOutIds)
                .update();
    }

    @Override
    public void warehouseOutShip(WarehouseOutShipDTO shipDTO) {
        lambdaUpdate()
                .set(OrderDO::getStatus, OrderStatusEnum.COMPLETE)
                .set(OrderDO::getShipFlag, true)
                .set(OrderDO::getShipTime, DateUtil.getDateline())
                .set(OrderDO::getLogisticsCompanyId, shipDTO.getLogisticsCompanyId())
                .set(OrderDO::getLogisticsCompanyName, shipDTO.getLogisticsCompanyName())
                .set(OrderDO::getLogisticsTrackingNumber, shipDTO.getTrackingNumber())
                .eq(OrderDO::getWarehouseOutId, shipDTO.getId())
                .update();
    }

    @Override
    public void submit(List<Long> ids) {
        List<OrderDO> orderList = listByIds(ids);
        for (OrderDO orderDO : orderList) {
            if (!new OrderAllowable(orderDO).getSubmit()) {
                throw new ServiceException(StrUtil.format("订单【{}】不允许进行提交操作", orderDO.getSn()));
            }
        }

        lambdaUpdate().set(OrderDO::getStatus, OrderStatusEnum.WAIT_AUDIT).in(OrderDO::getId, ids).update();
    }

    @Override
    public void withdraw(List<Long> ids) {
        List<OrderDO> orderList = listByIds(ids);
        for (OrderDO orderDO : orderList) {
            if (!new OrderAllowable(orderDO).getWithdraw()) {
                throw new ServiceException(StrUtil.format("订单【{}】不允许进行撤回操作", orderDO.getSn()));
            }
        }

        lambdaUpdate().set(OrderDO::getStatus, OrderStatusEnum.WAIT_SUBMIT).in(OrderDO::getId, ids).update();
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void audit(List<Long> ids, Boolean isPass, String remark) {
        List<OrderDO> orderList = listByIds(ids);
        for (OrderDO orderDO : orderList) {
            if (!new OrderAllowable(orderDO).getAudit()) {
                throw new ServiceException(StrUtil.format("订单【{}】不允许进行审核操作", orderDO.getSn()));
            }
        }

        for (OrderDO orderDO : orderList) {
            // 更新订单状态
            OrderStatusEnum status = converter.convertAuditStatus(isPass, orderDO.getType());
            lambdaUpdate().set(OrderDO::getStatus, status).eq(OrderDO::getId, orderDO.getId()).update();

            // 零售订单审核通过后，扣减库存
            if (isPass && orderDO.getType() == OrderTypeEnum.TO_C) {
                List<OrderItemDO> itemList = orderItemManager.listByOrderIds(Collections.singletonList(orderDO.getId()));
                List<StockUpdateDTO> stockUpdateList = itemConverter.convertStockUpdate(itemList);
                warehouseEntryBatchManager.updateStock(StockChangeSourceEnum.RETAIL_ORDER, orderDO.getSn(), stockUpdateList);
            }

            // 审核通过后，生成财务明细
            if (isPass) {
                financeItemManager.addIncome(FinanceIncomeTypeEnum.OrderSale, orderDO.getSn(), CurrencyUtil.add(orderDO.getPayPrice(), orderDO.getDiscountPrice()));
                if (orderDO.getDiscountPrice() > 0) {
                    financeItemManager.addExpand(FinanceExpandTypeEnum.Promotion, orderDO.getSn(), orderDO.getDiscountPrice());
                }
            }
        }
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void payment(List<Long> ids) {
        List<OrderDO> orderList = listByIds(ids);
        for (OrderDO orderDO : orderList) {
            if (!new OrderAllowable(orderDO).getPayment()) {
                throw new ServiceException(StrUtil.format("订单【{}】不允许进行支付操作", orderDO.getSn()));
            }
        }

        lambdaUpdate()
                .set(OrderDO::getPaymentStatus, OrderPaymentStatusEnum.PAY)
                .set(OrderDO::getPaymentTime, DateUtil.getDateline())
                .in(OrderDO::getId, ids)
                .update();
    }

    private void check(OrderDTO orderDTO) {
        // 校验会员
        checkMember(orderDTO);

        // 校验企业
        checkEnterprise(orderDTO);

        // 校验仓库
        checkWarehouse(orderDTO);

        // 校验部门
        checkDept(orderDTO);

        // 校验销售经理
        checkMarketing(orderDTO);

        // 校验门店
        checkStore(orderDTO);

        // 校验配送地区
        checkShipRegion(orderDTO);

        // 校验库存批次
        checkBatch(orderDTO);

        // 校验商品
        checkProduct(orderDTO);

        // 校验分类
        checkCategory(orderDTO);

        // 校验收款账户
        checkPaymentAccount(orderDTO);
    }

    private void checkShipRegion(OrderDTO orderDTO) {
        if (orderDTO.getType() != OrderTypeEnum.TO_B || orderDTO.getDeliveryType() != OrderDeliveryType.express) {
            return;
        }

        Region region = new RegionFormatter(regionsManager).parse(orderDTO.getShipRegionId().toString(), Locale.CHINA);
        orderDTO.setRegion(region);
    }

    private void checkCategory(OrderDTO orderDTO) {
        List<Long> categoryIds = orderDTO.getType() == OrderTypeEnum.TO_C ? convertList(orderDTO.getBatchMap().values(), WarehouseEntryBatchDO::getCategoryId) :
                convertList(orderDTO.getProductMap().values(), ProductDO::getCategoryId);
        List<CategoryDO> categoryList = categoryManager.listByIds(categoryIds);
        // 暂时不做数据存在校验
        Map<Long, CategoryDO> categoryMap = convertMap(categoryList, CategoryDO::getCategoryId, Function.identity());
        orderDTO.setCategoryMap(categoryMap);
    }

    private void checkBatch(OrderDTO orderDTO) {
        if (orderDTO.getType() != OrderTypeEnum.TO_C) {
            return;
        }

        List<OrderItemDTO> itemList = orderDTO.getItemList();
        if (itemList.stream().anyMatch(orderItemDTO -> orderItemDTO.getBatchId() == null)) {
            throw new ServiceException("库存批次id不能为空");
        }
        List<Long> batchIds = convertList(itemList, OrderItemDTO::getBatchId);
        List<WarehouseEntryBatchDO> batchList = warehouseEntryBatchManager.listByIds(batchIds);
        Map<Long, WarehouseEntryBatchDO> batchMap = convertMap(batchList, WarehouseEntryBatchDO::getId, Function.identity());

        for (Long batchId : batchIds) {
            WarehouseEntryBatchDO batchDO = batchMap.get(batchId);
            if (batchDO == null) {
                throw new ServiceException(StrUtil.format("库存批次id：{} 不存在", batchId));
            }
            if (!batchDO.getWarehouseId().equals(orderDTO.getWarehouseId())) {
                throw new ServiceException(StrUtil.format("库存批次：{} 不属于当前选择的仓库", batchDO.getSn()));
            }
        }

        orderDTO.setBatchMap(batchMap);
    }

    private void checkEnterprise(OrderDTO orderDTO) {
        if (orderDTO.getType() != OrderTypeEnum.TO_B || orderDTO.getSource() != OrderSourceEnum.Internal) {
            return;
        }
        EnterpriseDO enterpriseDO = enterpriseManager.getById(orderDTO.getOwnerId());
        if (enterpriseDO == null) {
            throw new ServiceException("企业不存在");
        }
        orderDTO.setEnterpriseDO(enterpriseDO);
    }

    private void checkDept(OrderDTO orderDTO) {
        if (orderDTO.getSource() == OrderSourceEnum.External) {
            return;
        }
        DeptDO deptDO = deptManager.getById(orderDTO.getWarehouseDO().getDeptId());
        if (deptDO == null) {
            throw new ServiceException("仓库未关联部门，请先进行关联部门操作");
        }
        orderDTO.setDeptDO(deptDO);
    }

    private void checkPaymentAccount(OrderDTO orderDTO) {
        if (orderDTO.getSource() != OrderSourceEnum.Internal) {
            return;
        }

        List<OrderPaymentDTO> paymentList = orderDTO.getPaymentList();
        List<Long> collectingAccountIds = convertList(paymentList, OrderPaymentDTO::getCollectingAccountId);
        Map<Long, CollectingAccountDO> collectingAccountMap = collectingAccountManager.listAndConvertMap(collectingAccountIds, CollectingAccountDO::getId);
        for (OrderPaymentDTO orderPaymentDTO : paymentList) {
            CollectingAccountDO collectingAccountDO = collectingAccountMap.get(orderPaymentDTO.getCollectingAccountId());
            if (collectingAccountDO == null) {
                throw new ServiceException(StrUtil.format("收款账户【{}】不存在", orderPaymentDTO.getCollectingAccountId()));
            }
            if (!collectingAccountDO.getEnableFlag()) {
                throw new ServiceException(StrUtil.format("收款账户【{}】未启用", collectingAccountDO.getName()));
            }
        }
        orderDTO.setCollectingAccountMap(collectingAccountMap);
    }

    private void checkProduct(OrderDTO orderDTO) {
        if (orderDTO.getType() != OrderTypeEnum.TO_B) {
            return;
        }

        if (orderDTO.getItemList().stream().anyMatch(orderItemDTO -> orderItemDTO.getProductId() == null)) {
            throw new ServiceException("产品id不能为空");
        }
        // 批量查询商品
        List<Long> productIds = convertList(orderDTO.getItemList(), OrderItemDTO::getProductId);
        Map<Long, ProductDO> productMap = productManager.listAndConvertMap(productIds, ProductDO::getId);
        // 循环校验
        for (OrderItemDTO orderItemDTO : orderDTO.getItemList()) {
            ProductDO productDO = productMap.get(orderItemDTO.getProductId());
            if (productDO == null) {
                throw new ServiceException(StrUtil.format("商品【{}】不存在", orderItemDTO.getProductId()));
            }
            // 第三方系统订单还需要校验商品金额
            if (orderDTO.getSource() == OrderSourceEnum.External) {
                if (!productDO.getPrice().equals(orderItemDTO.getPrice())) {
                    throw new ServiceException(StrUtil.format("商品【{}】价格已变更为【{}】", productDO.getName(), productDO.getPrice()));
                }
            }
        }

        orderDTO.setProductMap(productMap);
    }

    private void checkStore(OrderDTO orderDTO) {
        if (orderDTO.getDeliveryType() != OrderDeliveryType.self_pick) {
            return;
        }

        StoreDO storeDO = storeManager.getById(orderDTO.getStoreId());
        if (storeDO == null || storeDO.getDeleteFlag() == 1) {
            throw new ServiceException("自提门店不存在");
        }
        orderDTO.setStoreDO(storeDO);
    }

    private void checkMarketing(OrderDTO orderDTO) {
        if (orderDTO.getMarketingId() == null) {
            return;
        }

        MarketingManagerDO marketingManagerDO = marketingManagerManager.getById(orderDTO.getMarketingId());
        if (marketingManagerDO == null) {
            throw new SecurityException("销售经理不存在");
        }
        if (marketingManagerDO.getDisableFlag() == 1) {
            throw new SecurityException("销售经理已禁用");
        }
        orderDTO.setMarketingManagerDO(marketingManagerDO);
    }

    private void checkWarehouse(OrderDTO orderDTO) {
        if (orderDTO.getSource() == OrderSourceEnum.External) {
            return;
        }
        WarehouseDO warehouseDO = warehouseManager.getById(orderDTO.getWarehouseId());
        if (warehouseDO == null) {
            throw new SecurityException("仓库不存在");
        }
        orderDTO.setWarehouseDO(warehouseDO);
    }

    private void checkMember(OrderDTO orderDTO) {
        if (orderDTO.getType() != OrderTypeEnum.TO_C) {
            return;
        }

        MemberDO memberDO = memberManager.getById(orderDTO.getOwnerId());
        if (memberDO == null) {
            throw new SecurityException("会员不存在");
        }
        if (memberDO.getDisableFlag()) {
            throw new SecurityException("会员已禁用");
        }
        orderDTO.setMemberDO(memberDO);
    }
}

